package edu.gatech.gro;

import java.util.ArrayList;
import java.util.List;

import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;

import android.app.Application;
import edu.gatech.gro.http.HttpRequest;
import edu.gatech.gro.http.IResponseAdapter;
import edu.gatech.gro.model.GroceryStore;
import edu.gatech.gro.model.Item;
import edu.gatech.gro.model.ItemType;
import edu.gatech.gro.model.ListGroup;
import edu.gatech.gro.model.ListItem;
import edu.gatech.gro.model.NamedList;
import edu.gatech.gro.model.ObjectResponse;
import edu.gatech.gro.model.User;
import edu.gatech.gro.model.dao.GroceryStoreDao;
import edu.gatech.gro.model.dao.ItemDao;
import edu.gatech.gro.model.dao.ItemTypeDao;
import edu.gatech.gro.model.dao.ListItemDao;
import edu.gatech.gro.model.dao.NamedListDao;
import edu.gatech.gro.model.dao.UserDao;
import edu.gatech.gro.model.parser.GroceryStoreJsonHandler;
import edu.gatech.gro.model.parser.ItemJsonHandler;
import edu.gatech.gro.model.parser.ItemTypeJsonHandler;
import edu.gatech.gro.model.parser.JsonHandler;
import edu.gatech.gro.model.parser.ListGroupItemJsonHandler;
import edu.gatech.gro.model.parser.NamedListJsonHandler;
import edu.gatech.gro.model.parser.UserJsonHandler;
import edu.gatech.gro.utils.JSONHelper;
import edu.gatech.gro.utils.Utils;

public class GroceryListManagerApplication extends Application {

	private static final String TAG = "GROCERY_LIST_MANAGER_APPLICATION";

	private static final String ROOT_URL = "http://www.fabienrenaud.com/gro2/";
	private static final String REQ_LOGIN = ROOT_URL + "login.php";
	// private static final String REQ_LOGOUT = ROOT_URL + "logout.php";
	private static final String REST_URL = ROOT_URL + "rest/";
	private static final String REQ_USER = REST_URL + "user";
	private static final String REST_SEARCH = REST_URL + "search";
	private static final String REST_ITEM = REST_URL + "item";
	private static final String REST_GROCERY_STORES = REST_URL + "grocery";
	private static final String REQ_NAMED_LIST = REQ_USER + "/list";
	private static final String REQ_SEARCH_ITEM = REST_SEARCH + "/item";
	private static final String REQ_ITEM_TYPES = REST_ITEM + "/type";

	private UserDao userDao;
	private NamedListDao namedListDao;
	private ListItemDao listItemDao;
	private GroceryStoreDao groceryStoreDao;
	private ItemTypeDao itemTypeDao;
	private ItemDao itemDao;

	public GroceryListManagerApplication() {
		if (Options.getInstance().isOffline()) {
			userDao = new UserDao(this);
			namedListDao = new NamedListDao(this);
			listItemDao = new ListItemDao(this);
			itemDao = new ItemDao(this);
			groceryStoreDao = new GroceryStoreDao(this);
			itemTypeDao = new ItemTypeDao(this);
		}
	}

	public ArrayList<User> getAllUsers() {
		if (userDao != null) {
			return userDao.getAllUsers();
		}
		return null;
	}

	public void getAllUsers(IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(getAllUsers(), responseAdapter);
		} else {
			HttpRequest r = new HttpRequest(HttpRequest.GET, REQ_USER, new UserJsonHandler());
			r.setResponseListener(responseAdapter);
			new Thread(r).start();
		}
	}

	public ArrayList<NamedList> getAllNamedList(final int userId) {
		if (userId > 0 && namedListDao != null) {
			return namedListDao.getAllNamedLists(userId);
		}
		return null;
	}

	public void getAllNamedList(final int userId, IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(getAllNamedList(userId), responseAdapter);
		} else {
			HttpRequest r = new HttpRequest(HttpRequest.GET, REQ_NAMED_LIST, new NamedListJsonHandler());
			r.setResponseListener(responseAdapter);
			new Thread(r).start();
		}
	}

	public ArrayList<ItemType> getAllItemTypes() {
		if (itemTypeDao != null) {
			return itemTypeDao.getAllItemTypes();
		}
		return null;
	}

	public void getAllItemTypes(IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(getAllItemTypes(), responseAdapter);
		} else {
			HttpRequest r = new HttpRequest(HttpRequest.GET, REQ_ITEM_TYPES, new ItemTypeJsonHandler());
			r.setResponseListener(responseAdapter);
			new Thread(r).start();
		}
	}

	public ArrayList<GroceryStore> getAllGroceryStores() {
		if (groceryStoreDao != null) {
			return groceryStoreDao.getAllGroceryStores();
		}
		return null;
	}

	public void getAllGroceryStores(IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(getAllGroceryStores(), responseAdapter);
		} else {
			HttpRequest r = new HttpRequest(HttpRequest.GET, REST_GROCERY_STORES, new GroceryStoreJsonHandler());
			r.setResponseListener(responseAdapter);
			new Thread(r).start();
		}
	}

	public NamedList getNamedList(final int userId, final String listNameClean) {
		NamedList list = null;
		if (namedListDao != null) {
			list = namedListDao.getNamedListByNameClean(userId, listNameClean);
			if (list != null) {
				ArrayList<ListGroup> groups = namedListDao.getAllItems(list.getId());
				list.setItemGroups(groups);
			}
		}
		return list;
	}

	public void getNamedList(final int userId, final String listNameClean, IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(getNamedList(userId, listNameClean), responseAdapter);
		} else {
			if (listNameClean != null) {
				String url = REQ_NAMED_LIST + "/" + listNameClean;
				HttpRequest r = new HttpRequest(HttpRequest.GET, url, new NamedListJsonHandler());
				r.setResponseListener(responseAdapter);
				new Thread(r).start();
			}
		}
	}

	public void searchItem(String itemNameClean, IResponseAdapter responseAdapter) {
		String url = REQ_SEARCH_ITEM + "/" + itemNameClean;
		HttpRequest r = new HttpRequest(HttpRequest.GET, url, new ListGroupItemJsonHandler());
		r.setResponseListener(responseAdapter);
		new Thread(r).start();
	}

	public boolean saveUser(final User user) {
		if (userDao != null) {
			return userDao.saveUser(user);
		}
		return false;
	}

	public void saveUser(final User user, IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(saveUser(user), responseAdapter);
		} else {
			if (user != null) {
				HttpRequest r = new HttpRequest(HttpRequest.POST, REQ_USER, new UserJsonHandler());
				r.setResponseListener(responseAdapter);
				r.setContent(JSONHelper.getJSON(user));
				new Thread(r).start();
			}
		}
	}

	public boolean saveNamedList(final NamedList namedList) {
		if (namedListDao != null) {
			return namedListDao.saveNamedList(namedList);
		}
		return false;
	}

	public void saveNamedList(final NamedList namedList, IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(saveNamedList(namedList), responseAdapter);
		} else {
			if (namedList != null) {
				HttpRequest r = new HttpRequest(HttpRequest.POST, REQ_NAMED_LIST, new NamedListJsonHandler());
				r.setResponseListener(responseAdapter);
				r.setContent(JSONHelper.getJSON(namedList));
				new Thread(r).start();
			}
		}
	}

	public boolean saveItem(final Item item) {
		if (itemDao != null) {
			return itemDao.saveItem(item);
		}
		return false;
	}

	public void saveItem(final Item item, IResponseAdapter responseAdapter) {
		if (item != null) {
			if (Options.getInstance().isOffline()) {
				sendResponse(saveItem(item), item, responseAdapter);
			} else {
				HttpRequest r = new HttpRequest(HttpRequest.POST, REST_ITEM, new ItemJsonHandler());
				r.setResponseListener(responseAdapter);
				r.setContent(JSONHelper.getJSONItem(item));
				new Thread(r).start();
			}
		}
	}

	public boolean addItemToNamedList(final ListItem item) {
		if (listItemDao != null) {
			return listItemDao.saveListItem(item);
		}
		return false;
	}

	public void addItemToNamedList(final String namedListNameClean, final ListItem item, IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(addItemToNamedList(item), item, responseAdapter);
		} else {
			if (item.getNamedListId() > 0 && namedListNameClean != null && item != null) {
				String url = REQ_NAMED_LIST + "/" + namedListNameClean;
				HttpRequest r = new HttpRequest(HttpRequest.POST, url, new JsonHandler());
				r.setResponseListener(responseAdapter);
				r.setContent(JSONHelper.getJSON(item));
				new Thread(r).start();
			}
		}
	}

	public boolean updateUser(final User user) {
		if (userDao != null) {
			return userDao.updateUser(user);
		}
		return false;
	}

	public void updateUser(final User user, IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(updateUser(user), user, responseAdapter);
		} else {
			if (user != null) {
				String url = REQ_USER + "/" + user.getId();
				HttpRequest r = new HttpRequest(HttpRequest.PUT, url, new JsonHandler());
				r.setContent(JSONHelper.getJSON(user));
				r.setResponseListener(responseAdapter);
				new Thread(r).start();
			}
		}
	}

	public boolean updateNamedList(final NamedList list) {
		if (namedListDao != null) {
			return namedListDao.updateNamedList(list);
		}
		return false;
	}

	public void updateNamedList(final NamedList list, IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(updateNamedList(list), list, responseAdapter);
		} else {
			if (list != null) {
				String url = REQ_NAMED_LIST + "/" + list.getId();
				HttpRequest r = new HttpRequest(HttpRequest.PUT, url, new NamedListJsonHandler());
				r.setResponseListener(responseAdapter);
				r.setContent(JSONHelper.getJSON(list));
				new Thread(r).start();
			}
		}
	}

	public boolean updateListItem(final ListItem item) {
		if (listItemDao != null) {
			return listItemDao.updateListItem(item);
		}
		return false;
	}

	public void updateListItem(final String namedListNameClean, final ListItem item, IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(updateListItem(item), item, responseAdapter);
		} else {
			if (item.getNamedListId() > 0 && namedListNameClean != null && item != null) {
				String url = REQ_NAMED_LIST + "/" + namedListNameClean + "/" + item.getId();
				HttpRequest r = new HttpRequest(HttpRequest.PUT, url, new JsonHandler());
				r.setResponseListener(responseAdapter);
				r.setContent(JSONHelper.getJSON(item));
				new Thread(r).start();
			}
		}
	}

	public boolean deleteUser(final User user) {
		if (userDao != null) {
			return userDao.deleteUser(user);
		}
		return false;
	}

	public void deleteUser(final User user, IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(deleteUser(user), responseAdapter);
		} else {
			if (user != null) {
				String url = REQ_USER + "/" + user.getId();
				HttpRequest r = new HttpRequest(HttpRequest.DELETE, url, new JsonHandler());
				r.setResponseListener(responseAdapter);
				new Thread(r).start();
			}
		}
	}

	public boolean deleteNamedList(final NamedList list) {
		if (namedListDao != null) {
			return namedListDao.deleteNamedList(list);
		}
		return false;
	}

	public void deleteNamedList(final NamedList list, IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(deleteNamedList(list), responseAdapter);
		} else {
			if (list != null) {
				String url = REQ_NAMED_LIST + "/" + list.getId();
				HttpRequest r = new HttpRequest(HttpRequest.DELETE, url, new JsonHandler());
				r.setResponseListener(responseAdapter);
				new Thread(r).start();
			}
		}
	}

	public boolean deleteListItem(final ListItem item) {
		if (listItemDao != null) {
			return listItemDao.deleteListItem(item);
		}
		return false;
	}

	public void deleteListItem(final String listNameClean, final ListItem item, IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(deleteListItem(item), responseAdapter);
		} else {
			if (listNameClean != null && item != null) {
				String url = REQ_NAMED_LIST + "/" + listNameClean + "/" + item.getId();
				HttpRequest r = new HttpRequest(HttpRequest.DELETE, url, new JsonHandler());
				r.setResponseListener(responseAdapter);
				new Thread(r).start();
			}
		}
	}

	public User login(final String username, final String password) {
		User user = null;
		if (userDao != null) {
			user = userDao.getUserByUsernameAndPassword(username, Utils.hash(password));
		}
		return user;
	}

	public void login(final String username, final String password, IResponseAdapter responseAdapter) {
		if (Options.getInstance().isOffline()) {
			sendResponse(login(username, password), responseAdapter);
		} else {
			List<NameValuePair> params = new ArrayList<NameValuePair>();
			params.add(new BasicNameValuePair("username", username));
			params.add(new BasicNameValuePair("password", password));

			HttpRequest r = new HttpRequest(HttpRequest.POST, REQ_LOGIN, new UserJsonHandler());
			r.setResponseListener(responseAdapter);
			r.setContent(params);
			new Thread(r).start();
		}
	}

	private void sendResponse(Object o, IResponseAdapter responseAdapter) {
		if (responseAdapter != null) {
			ObjectResponse r = new ObjectResponse();
			r.setSuccess(o != null);
			r.setData(o);
			if (o == null) {
				List<String> errors = new ArrayList<String>();
				errors.add("Unexpected error while processing data.");
				r.setErrors(errors);
			}
			responseAdapter.onResponse(r);
		}
	}

	private void sendResponse(boolean success, IResponseAdapter responseAdapter) {
		if (responseAdapter != null) {
			ObjectResponse r = new ObjectResponse();
			r.setSuccess(success);
			if (!success) {
				List<String> errors = new ArrayList<String>();
				errors.add("Unexpected error while processing data.");
				r.setErrors(errors);
			}
			responseAdapter.onResponse(r);
		}
	}

	private void sendResponse(boolean success, Object o, IResponseAdapter responseAdapter) {
		if (responseAdapter != null) {
			ObjectResponse r = new ObjectResponse();
			r.setSuccess(success);
			if (success) {
				r.setData(o);
			} else {
				List<String> errors = new ArrayList<String>();
				errors.add("Unexpected error while processing data.");
				r.setErrors(errors);
			}
			responseAdapter.onResponse(r);
		}
	}

	private int synchronizationSteps;
	private final int synchronizationTotalSteps = 4;
	private boolean isSynchronizing = false;

	public void synchronize(final IResponseAdapter responseAdapter) {
		if (!Options.getInstance().isOffline()) {
			if (responseAdapter != null) {
				responseAdapter.onResponse(null);
			}
			return;
		}
		if (!isSynchronizing) {
			isSynchronizing = true;
			synchronizationSteps = 0;
			sendAndFetchUsers(new IResponseAdapter() {

				@Override
				public void onResponse(ObjectResponse response) {
					if (response.isSuccessful()) {
						clearAndUpdateAllUsers((ArrayList<User>) response.getData());
					}
					synchronizationSteps++;
					confirmEndOfSynchronization(responseAdapter);
				}
			});

			fetchGroceryStores(new IResponseAdapter() {

				@Override
				public void onResponse(ObjectResponse response) {
					if (response.isSuccessful()) {
						groceryStoreDao.upgrade((ArrayList<GroceryStore>) response.getData());
					}
					synchronizationSteps++;
					confirmEndOfSynchronization(responseAdapter);
				}
			});

			fetchItemTypes(new IResponseAdapter() {

				@Override
				public void onResponse(ObjectResponse response) {
					if (response.isSuccessful()) {
						itemTypeDao.upgrade((ArrayList<ItemType>) response.getData());
					}
					synchronizationSteps++;
					confirmEndOfSynchronization(responseAdapter);
				}
			});

			fetchItems(new IResponseAdapter() {

				@Override
				public void onResponse(ObjectResponse response) {
					if (response.isSuccessful()) {
						itemDao.upgrade((ArrayList<Item>) response.getData());
					}
					synchronizationSteps++;
					confirmEndOfSynchronization(responseAdapter);
				}
			});
		}
	}

	private void confirmEndOfSynchronization(final IResponseAdapter responseAdapter) {
		boolean doSend = true;
		if (isSynchronizing) {
			doSend = synchronizationSteps == synchronizationTotalSteps;
		}
		if (doSend) {
			isSynchronizing = false;
			if (responseAdapter != null) {
				responseAdapter.onResponse(null);
			}
		}
	}

	private void clearAndUpdateAllUsers(List<User> userList) {
		userDao.clear(); // should auto delete lists and items
		namedListDao.clear();
		listItemDao.clear();
		if (userList != null) {
			for (User user : userList) {
				userDao.saveUser(user);
				if (user.getLists() != null) {
					for (NamedList list : user.getLists()) {
						namedListDao.saveNamedList(list);
						if (list.getItemGroups() != null) {
							for (ListGroup group : list.getItemGroups()) {
								if (group.getItems() != null) {
									for (Item item : group.getItems()) {
										ListItem lItem = (ListItem) item;
										lItem.setNamedListId(list.getId());
										listItemDao.saveListItem(lItem);
									}
								}
							}
						}
					}
				}
			}
		}
	}

	private void sendAndFetchUsers(final IResponseAdapter responseAdapter) {
		ArrayList<User> users = getAllUsers();

		HttpRequest r = new HttpRequest(HttpRequest.POST, REQ_USER, new UserJsonHandler());
		r.addGetParameter("fullsync", "true");
		JSONArray arr = JSONHelper.getJSONUsers(users, this);
		// Log.d(TAG, arr.toString());
		r.setContent(arr);
		r.setResponseListener(responseAdapter);
		new Thread(r).start();
	}

	private void fetchGroceryStores(IResponseAdapter responseAdapter) {
		HttpRequest r = new HttpRequest(HttpRequest.GET, REST_GROCERY_STORES, new GroceryStoreJsonHandler());
		r.setResponseListener(responseAdapter);
		new Thread(r).start();
	}

	private void fetchItemTypes(IResponseAdapter responseAdapter) {
		HttpRequest r = new HttpRequest(HttpRequest.GET, REQ_ITEM_TYPES, new ItemTypeJsonHandler());
		r.setResponseListener(responseAdapter);
		new Thread(r).start();
	}

	private void fetchItems(IResponseAdapter responseAdapter) {
		HttpRequest r = new HttpRequest(HttpRequest.GET, REST_ITEM, new ItemJsonHandler());
		r.setResponseListener(responseAdapter);
		new Thread(r).start();
	}
}
